package compiler.scanner.nfa;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;

////////////////////////////////////////////////////////////////////////
//// File : NFAState.cs
////////////////////////////////////////////////////////////////////////
//// Version(Build Number) : 0.03
//// Creation Date : 25/11/2005.
//// Last modification data : 25/11/2005 8:18PM
//// Author name : Sherif Sakr.
//// Purpose : Representation of a single NFA state.
//// Change History :
//// Dependencies : None.
////////////////////////////////////////////////////////////////////////
/// <summary>
/// Summary description for NFAState.
/// </summary>
public class NFAState
{
    //~ Static fields/initializers ---------------------------------------------

    private static int NEXT_ID = 0;

    //~ Instance fields --------------------------------------------------------

    private ArrayList<NFATransition> transitions = new ArrayList<NFATransition>();
    private String                   token = null;
    private boolean                  acceptable = false;
    private final int                id = nextId();

    //~ Constructors -----------------------------------------------------------

    public NFAState() {}

    //~ Methods ----------------------------------------------------------------

    public static void reset()
    {
        NEXT_ID = 0;
    }

    public Set<NFAState> getClosureStatesOn(char c)
    {
        Set<NFAState> closureStates = new HashSet<NFAState>();
        closureStates.add(this);

        for (NFATransition nfaTransition : this.transitions)
        {
            if (!(nfaTransition instanceof ETransition) && (nfaTransition.getCharacter() == c))
            {
                for (NFAState destState : nfaTransition.getDestinations())
                {
                    closureStates.addAll(destState.getEClosureStates());
                }
            }
        }

        return (closureStates);
    }

    public Set<NFAState> getEClosureStates()
    {
        Set<NFAState> EClosureStates = new HashSet<NFAState>();
        EClosureStates.add(this);

        for (NFATransition nfaTransition : this.transitions)
        {
            if (nfaTransition instanceof ETransition)
            {
                for (NFAState destState : nfaTransition.getDestinations())
                {
                    EClosureStates.addAll(destState.getEClosureStates());
                }
            }
        }

        return (EClosureStates);
    }

    public int getId()
    {
        return id;
    }

    // / <summary>
    // / Description : Gets all NFA states in the NFA.
    // / </summary>
    // / <returns>ArrayList of all NFA states in the NFA.</returns>
    public ArrayList getNFAStates()
    {
        ArrayList<NFAState> closedNFAStates = new ArrayList<NFAState>();
        Queue<NFAState>     openNFAStates = new LinkedList<NFAState>();
        NFAState            currNFAState = null;

        openNFAStates.add(this);

        do
        {
            currNFAState = openNFAStates.remove();

            for (NFATransition nfaTransition : currNFAState.transitions)
            {
                for (NFAState nfaState : nfaTransition.getDestinations())
                {
                    if (!openNFAStates.contains(nfaState) && !closedNFAStates.contains(nfaState))
                    {
                        openNFAStates.offer(nfaState);
                    }
                }
            }

            closedNFAStates.add(currNFAState);
        }
        while (openNFAStates.size() != 0);

        return (closedNFAStates);
    }

    public ArrayList<NFATransition> getNonETransitions()
    {
        ArrayList<NFATransition> nonETransitions = new ArrayList<NFATransition>();

        for (NFATransition nfaTransition : this.transitions)
        {
            if (!(nfaTransition instanceof ETransition))
            {
                nonETransitions.add(nfaTransition);
            }
        }

        return (nonETransitions);
    }

    public Set<NFAState> getReachable()
    {
        Set<NFAState>   ret = new HashSet<NFAState>();
        Queue<NFAState> remaininig = new LinkedList<NFAState>();
        ret.add(this);
        remaininig.offer(this);

        while (!remaininig.isEmpty())
        {
            NFAState state = remaininig.remove();

            for (NFATransition trans : state.getTransitions())
            {
                for (NFAState target : trans.getDestinations())
                {
                    if (!ret.contains(target))
                    {
                        ret.add(target);
                        remaininig.offer(target);
                    }
                }
            }
        }

        return ret;
    }

    public String getToken()
    {
        return token;
    }

    public ArrayList<NFATransition> getTransitions()
    {
        return transitions;
    }

    public boolean isAcceptable()
    {
        return acceptable;
    }

    public void setAcceptable(boolean acceptable)
    {
        this.acceptable = acceptable;
    }

    public void setToken(String token)
    {
        this.token = token;
    }

    @Override
    public String toString()
    {
        return "" + getId();
    }

    private static synchronized int nextId()
    {
        return NEXT_ID++;
    }
}
